package engine.scene.tree;

import java.util.ArrayList;

import engine.collider.Intersector;
import engine.gameObject.GameObject;
import engine.scene.Frustum;
import engine.util.vector.Vector;

public class InnerNode extends Node{
	private Node[] nodes;
	private double[][] minPoints;
	
	public InnerNode(Frustum frustum, double[] min, double[] max) {
		super(frustum, min, max);
		
		nodes = new Node[8];
		minPoints = createMinPoints(min, getSize());
	}
	
	private double[][] createMinPoints(double[] min, double size) {
		double[][] minPoints = new double[8][];
		
		minPoints[0] = new double[]{min[0], min[1], min[2]};
		minPoints[1] = new double[]{min[0] + size / 2, min[1], min[2]};
		minPoints[2] = new double[]{min[0], min[1] + size / 2, min[2]};
		minPoints[3] = new double[]{min[0] + size / 2, min[1] + size / 2, min[2]};
		minPoints[4] = new double[]{min[0], min[1], min[2] + size / 2};
		minPoints[5] = new double[]{min[0] + size / 2, min[1], min[2] + size / 2};
		minPoints[6] = new double[]{min[0], min[1] + size / 2, min[2] + size / 2};
		minPoints[7] = new double[]{min[0] + size / 2, min[1] + size / 2, min[2] + size / 2};
		
		return minPoints;
	}
	
	public void setChild(int i, Node child) {
		if(i >= nodes.length) {
			throw new ArrayIndexOutOfBoundsException("Children are not allowed to have a higher index than 8");
		}
		
		nodes[i] = child;
	}

	@Override
	public void render(float timeElapsed) {
		if(getFrustum().cubeInFrustum(getMin()[0], getMin()[1], getMin()[2], getMax()[0], getMax()[1], getMax()[2])) {
			for(int i = 0; i < nodes.length; i++) {
				if(nodes[i] != null) {
					nodes[i].render(timeElapsed);
				}
			}
		}
	}

	@Override
	public void insert(GameObject staticObject) {
		for(int i = 0; i < minPoints.length; i++) {
			if(Intersector.intersectsBoxSphere(minPoints[i], getSize() / 2, Vector.add(staticObject.getPosition(), staticObject.getBoundingSphere().getPosition()), staticObject.getBoundingSphere().getRadius())) {
				if(nodes[i] == null) {
					if(Math.abs(getSize() / 2 - staticObject.getCurrentOctree().getMinSize()) < 0.00001) {
						nodes[i] = new Leaf(getFrustum(), minPoints[i], getMaxPoint(minPoints[i], getSize() / 2));
					} else {
						nodes[i] = new InnerNode(getFrustum(), minPoints[i], getMaxPoint(minPoints[i], getSize() / 2));
					}
				}
				nodes[i].insert(staticObject);
			}
		}
	}
	
	@Override
	public void getNearObjects(ArrayList<GameObject> objects, GameObject g) {
		for(int i = 0; i < nodes.length; i++) {
			if(nodes[i] != null && Intersector.intersectsBoxSphere(minPoints[i], getSize() / 2, Vector.add(g.getPosition(), g.getBoundingSphere().getPosition()), g.getBoundingSphere().getRadius())) {
				nodes[i].getNearObjects(objects, g);
			}
		}
	}
	
	private static double[] getMaxPoint(double[] min, double size) {
		double[] ret = new double[min.length];
		for(int i = 0; i < ret.length; i++) {
			ret[i] = min[i] + size;
		}
		return ret;
	}
	
	@Override
	public void reset() {
		for(int i = 0; i < nodes.length; i++) {
			if(nodes[i] != null) {
				nodes[i].reset();
			}
		}
	}
}
